﻿using AutoMapper;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
using YaAppoint.Appointments.Pools;
using YaAppoint.CustomSettings;
using YaAppoint.Devices;
using YaAppoint.ExamAppies;
using YaAppoint.Rules;

namespace YaAppoint.Appointments.AutoAppoint
{
    /// <summary>
    /// 预约计划服务
    /// </summary>
    [RemoteService(isEnabled:false)]
    public class AppointPlanAppService(IRepository<Pool,Guid> poolRepository,
                                       IMapper mapper,
                                       IRepository<DeviceType,Guid> deviceTypeRepository,
                                       IPoolAppService poolAppService,
                                       ISchemaFactory schemaFactory,
                                       AsyncQueryableExecuter queryableExecuter,
                                       ICustomSettingAppService customSettingAppService):ApplicationService,IAppointPlanAppService,ITransientDependency
                                            
    {
        private readonly IRepository<Pool, Guid> _poolRepository=poolRepository;
        private readonly ICustomSettingAppService _customSettingAppService=customSettingAppService;
        private readonly IRepository<DeviceType, Guid> _deviceTypeRepository = deviceTypeRepository;
        private readonly ISchemaFactory _schemeFactory=schemaFactory;
        private readonly AsyncQueryableExecuter _queryableExecuter=queryableExecuter;
        private readonly IMapper _mapper = mapper;
        private readonly IPoolAppService _poolAppService=poolAppService;
        /// <summary>
        /// 统计设备对申请项目的支持程度
        /// (纯内存操作)
        /// </summary>
        /// <param name="applyItems">申请项目</param>
        /// <param name="deviceList">设备类型下所有设备</param>
        /// <returns></returns>
        public DegreeFilter CalculateDeviceSupportDegree(List<ExamApplicationDto> applyItems, List<DeviceDto> deviceList)
        {
            var res = new DegreeFilter();
            res.AllDegrees = new List<SupportDegree>();         // 所有设备的支持度统计集合
            res.AllSupporteds = new List<SupportDegree>();      // 支持所有申请项目的设备集合
            res.BaseLineSupporteds = new List<SupportDegree>(); // 支持基础项目的设备集合
            res.NotSupportBaseLine = new List<SupportDegree>(); // 不支持基准项目的设备集合
            res.BaseLineApplyItem = applyItems!.Where(item=>item.ExamItem.Devices!=null && item.ExamItem.Devices.Count>0)
                                               .OrderByDescending(item => item.ExamItem.Order).ToList().FirstOrDefault()!; // 基准申请项目
            if (res.BaseLineApplyItem == null)
            {
                res.BaseLineApplyItem = applyItems[0];
            }

            foreach (var device in deviceList)
            {
                var degree = new SupportDegree(deviceId: device.Id!.Value,
                                               device: device,
                                               suppotedApplyItems: new List<ExamApplicationDto>(),
                                               isAllSupport: false,
                                               suportDegree: 0);
                foreach (var item in applyItems) // 申请项目
                {
                    var examItem = item.ExamItem; // 检查项目
                    if (examItem.Devices != null && examItem.Devices.Count > 0)
                    {
                        if (examItem.Devices.Contains(device.Id!.Value))
                        {
                            degree.SuppotedApplyItems.Add(item);
                            degree.SuportDegree += (int)examItem.Order;
                        }
                    }
                    else if (examItem.Devices == null || examItem.Devices.Count == 0)
                    {
                        degree.SuppotedApplyItems.Add(item);
                        degree.SuportDegree += (int)examItem.Order;
                    }
                }

                // 是否支持全部申请项目
                if (degree.SuppotedApplyItems.Count == applyItems.Count)
                {
                    degree.IsAllSupport = true;// 支持全部申请项目
                    res.AllSupporteds.Add(degree);
                }

                //  是否支持基准项目
                if (degree.SuppotedApplyItems.Contains(res.BaseLineApplyItem))
                {
                    res.BaseLineSupporteds.Add(degree);
                }
                else
                {
                    res.NotSupportBaseLine.Add(degree);
                }
                res.AllDegrees.Add(degree);
            }
            return res;
        }


        /// <summary>
        /// 统计检查项目对时间要求、
        /// 是否彼此冲突、涉及冲突的检查项目
        /// </summary>
        /// <param name="applyItems"></param>
        /// <returns></returns>
        public TimeFilter GenerateTimeIntervalFilter(List<ExamApplicationDto> applyItems)
        {
            var filter = new TimeFilter();
            filter.AllTimeRequires = new List<TimeRequire>();
            filter.TimeRequires = new List<TimeRequire>();
            filter.Conflicts = new List<Conflict>();
            foreach (var applyItem in applyItems)
            {
                var require = new TimeRequire();
                var examItem = applyItem.ExamItem;
                require.ApplyItem = applyItem;
                require.IsTimeIntervalRequired = examItem.RequireInterval;
                if (require.IsTimeIntervalRequired)
                {
                    require.StartTime = examItem.StartTime;
                    require.EndTime = examItem.EndTime;
                    filter.TimeRequires.Add(require); // 有具体时间要求的项目
                }
                filter.AllTimeRequires.Add(require);  // 所有申请项目的时间情况
            }

            // 检测冲突、生成冲突记录、计算可能涉及冲突的申请项目
            if (filter.TimeRequires != null && filter.TimeRequires.Count > 2)
            {
                filter.IsTimeRequired = true;
                CalculateConflict(filter); // 检测时间冲突，生成冲突记录
            }
            return filter;
        }


        /// <summary>
        /// 检测冲突
        /// 如有冲突，记录冲突、涉及冲突的检查项目、
        /// 如无冲突，计算出可预约时间段
        /// </summary>
        public void CalculateConflict(TimeFilter filter)
        {
            var timeRequires = filter.TimeRequires;// 有特殊时间要求的项目
            foreach (var require in timeRequires!)
            {
                var otherRequires = timeRequires.Where(r => r.ApplyItem.Id != require.ApplyItem.Id);
                foreach (var otherRequire in otherRequires)
                {
                    if (require.EndTime > otherRequire.StartTime || require.StartTime < otherRequire.EndTime)
                    {
                        var conflict = new Conflict();
                        conflict.Require1 = require;
                        conflict.Require2 = otherRequire;
                        // 判断冲突是否已记录
                        var exsitConflic = filter.Conflicts.FirstOrDefault(c => (c.Require1 == require && c.Require2 == otherRequire) || (c.Require1 == otherRequire && c.Require2 == require));

                        if (exsitConflic == null)
                        {
                            filter.Conflicts.Add(conflict);

                            // 判断是否记录冲突项目
                            if (!filter.ConflictInvolvedRequires.Contains(require))
                            {
                                filter.ConflictInvolvedRequires.Add(require);
                            }

                            if (!filter.ConflictInvolvedRequires.Contains(otherRequire))
                            {
                                filter.ConflictInvolvedRequires.Add(otherRequire);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 生成单个预约计划
        /// （针对自动预约后修改）
        ///  当医生需要修改自动预约方案时，选中申请项目(一个或多个)选中日期需要自动根据规则推荐可选号池时，使用本函数
        /// </summary>
        /// <param name="degreeFilter">设备支持情况统计对象</param>
        /// <param name="timeFilter">申请项目时间要求统计对象</param>
        /// <returns></returns>
        public AppointPlan? GenerateAppointPlan(DegreeFilter degreeFilter, TimeFilter timeFilter, List<ExamApplicationDto> applyItems)
        {
            // 检查项目是否有时间上的冲突
            if (timeFilter.IsConflicting)
            {
                return null;
            }

            // 检查是否有设备支持全部检查项目
            if (degreeFilter.AllSupporteds == null || degreeFilter.AllSupporteds.Count == 0)
            {
                return null;
            }
            
            // (无时间冲突、有设备能支持全部申请项目的检查)
            var appointPlan = new AppointPlan();
            appointPlan.SuggestedPools = new List<PoolDto>();
            appointPlan.Devices = degreeFilter.AllSupporteds.Select(supportDegree => supportDegree.Device).ToList(); // 可以预约哪些设备
            appointPlan.ApplyItems = applyItems;  // 要预约的申请项目 

            // 预约的时间段
            if (timeFilter.IsTimeRequired && (timeFilter.TimeRequires != null && timeFilter.TimeRequires.Count > 0)) 
            {
                appointPlan.IsTimeRequired = true;
                //获取有时间要求的项目中的最早开始时间
                appointPlan.StartTime = timeFilter.BaseLineApplyItem.ExamItem.StartTime!.Value;
                appointPlan.EndTime = timeFilter.BaseLineApplyItem.ExamItem.EndTime!.Value;
            }
            return appointPlan;
        }


        /// <summary>
        /// 生成预约计划列表
        /// (针对自动生成预约方案)
        /// </summary>
        /// <param name="degreeFilter"></param>
        /// <param name="timeFilter"></param>
        /// <returns></returns>
        public async Task<List<AppointPlan>> GenerateAppointPlans(DegreeFilter degreeFilter, List<ExamApplicationDto> dtApplyItems,CustomSettingDto globalSetting)
        {
            List<AppointPlan> apppoinPlanList = new List<AppointPlan>();

            if (degreeFilter.AllSupporteds == null || degreeFilter.AllSupporteds.Count == 0)// 无设备支持全部检查项目
            {
                Logger.LogInformation("无设备可支持设备类型下所有项目");
                // 贪心算法划分在哪台设备上检查哪些项目
                var selectedDevices = PlanDeviceAndApplyItem(degreeFilter, dtApplyItems); // (纯内存，无数据库访问)
                foreach (var device in selectedDevices)
                {
                    var devices = new List<SupportDegree>();
                    devices!.Add(degreeFilter.AllDegrees!.First(degree => degree.Device.Id == device.DeviceId));
                    var planList =await GetAppointPlansByTimeConfilicts(device.ApplyItems, devices,globalSetting);
                    apppoinPlanList.AddRange(planList);
                }
            }
            else 
            {
                // 存在支持所有申请项目的设备
                Logger.LogInformation("存在设备支持设备类型下所有项目");
                List<SupportDegree> devices = degreeFilter.AllSupporteds;
                apppoinPlanList.AddRange(await GetAppointPlansByTimeConfilicts(dtApplyItems,degreeFilter.AllSupporteds,globalSetting));
            }
            return apppoinPlanList;
        }

        /// <summary>
        /// 创建预约计划
        /// （根据时间划分）
        /// </summary>
        /// <param name="applyItems"></param>
        /// <param name=""></param>
        /// <returns></returns>
        public async Task<List<AppointPlan>> GetAppointPlansByTimeConfilicts(List<ExamApplicationDto> applyItems,List<SupportDegree> devices,CustomSettingDto globalSetting)
        {
            var appointPlanList= new List<AppointPlan>();
            var timeFilter = GenerateTimeIntervalFilter(applyItems);
            var confilicts = timeFilter.Conflicts;
            if (timeFilter.IsConflicting) // 如果有时间冲突
            {
                Logger.LogInformation("申请项目包含时间冲突");
                var remainInvolvedRequires = new List<TimeRequire>();
                remainInvolvedRequires.AddRange(timeFilter.ConflictInvolvedRequires);
                // 遍历所有涉及冲突的申请项目
                foreach (var timeRquire in timeFilter.ConflictInvolvedRequires)
                {
                    // 如果项目未被加入计划
                    if (appointPlanList.Where(plan => plan.ApplyItems.Contains(timeRquire.ApplyItem)).ToList().Count == 0)
                    {
                        remainInvolvedRequires.Remove(timeRquire);
                        var plan = new AppointPlan();
                        plan.ApplyItems = new List<ExamApplicationDto>();
                        plan.ApplyItems!.Add(timeRquire.ApplyItem);
                        plan.IsTimeRequired = true;
                        plan.StartTime = timeRquire.StartTime;
                        plan.EndTime = timeRquire.EndTime;
                        plan.Devices = devices.Select(s => s.Device).ToList();
                        foreach (var reqiure in remainInvolvedRequires)
                        {
                            if (confilicts.Where(c => (c.Require1 == timeRquire && c.Require2 == reqiure) || (c.Require2 == reqiure && c.Require1 == timeRquire)).ToList().Count == 0)
                            {
                                plan.ApplyItems.Add(reqiure.ApplyItem);
                            }
                        }
                        plan.SuggestedPools = await GetSuggestPools(plan,globalSetting);
                        appointPlanList.Add(plan);
                    }
                }
            }
            else // 无时间冲突
            {
                Logger.LogInformation("设备类型下申请项目无时间冲突");
                var plan = new AppointPlan();
                plan.ApplyItems = applyItems;
                plan.IsTimeRequired = false;
                plan.StartTime = null;
                plan.EndTime = null;
                plan.Devices = devices.Select(d => d.Device).ToList();
                plan.SuggestedPools = await GetSuggestPools(plan,globalSetting);
                appointPlanList.Add(plan);
            }
            return appointPlanList;
        }



        /// <summary>
        /// 统计设备预约情况
        /// </summary>
        /// <param name="devices"></param>
        /// <returns></returns>

        public async Task<List<DeviceStatus>> CalculateDeviceStatus(List<DeviceDto> devices)
        {
            var res = new List<DeviceStatus>();
            var globalSetting = await _customSettingAppService.GetGlobalSetting();
            var appointableDayCount = globalSetting!.AppointableCount != null ? globalSetting!.AppointableCount : 3;
            foreach (var device in devices)
            {
                var deviceStatus = new DeviceStatus();
                deviceStatus.DeviceId = device!.Id!.Value;
                deviceStatus.StatusList = new List<DeviceStatusByDay>();
                var date = DateTime.Now.Date;
                var maxDate = date.AddDays((double)(appointableDayCount - 1));
                while (date.Date <= maxDate)
                {
                    var poolsToday = await _poolRepository.GetListAsync(p => p.Date.Date == date.Date
                                                                       && p.DeviceId == device!.Id!.Value
                                                                       && p.IsActive);
                    var deviceStatusByDay = new DeviceStatusByDay();
                    deviceStatusByDay.MaxCount = poolsToday.Sum(p => p.MaxCount);
                    deviceStatusByDay.CurrentCount = poolsToday.Sum(p => p.Current);
                    deviceStatusByDay.Date = date.Date;
                    double percent = (double)(deviceStatusByDay.CurrentCount / deviceStatusByDay.MaxCount) * 100;
                    deviceStatusByDay.Percent = (int)Math.Round(percent);
                    deviceStatus.StatusList.Add(deviceStatusByDay);
                }
            }
            return res;
        }


        /// <summary>
        /// 规划用哪些设备，检查哪些项目
        /// 贪心算法，使用最少的设备覆盖所有检查项目
        /// </summary>
        /// <param name="degreeFilter">设备对申请项目的支持情况统计</param>
        /// <param name="dtApplyItems">当前设备类型下所有申请项目</param>
        /// <returns></returns>
        public List<SelectedDevice> PlanDeviceAndApplyItem(DegreeFilter degreeFilter, List<ExamApplicationDto> dtApplyItems)
        {
            var uncoveredItems = new List<ExamApplicationDto>();
            uncoveredItems.AddRange(dtApplyItems);          // 申请项目

            var selectedDevices = new List<SelectedDevice>();
            var unSelectedDegrees = degreeFilter.AllDegrees; // 可供选择的设备
            while (uncoveredItems.Count > 0)
            {
                SupportDegree bestDevice = unSelectedDegrees![0];               // 覆盖项目最多的设备
                int bestCoveredCount = 0;                // 最多覆盖多少项目
                List<ExamApplicationDto> bestCoveredItems = new List<ExamApplicationDto>();  // 覆盖的项目集合

                // 遍历所有设备，找到能覆盖最多项目的设备
                foreach (var supportDegree in degreeFilter.AllDegrees!)
                {
                    // 如果设备已被选中，则跳过
                    if (selectedDevices.Select(sd => sd.DeviceId).ToList().Contains(supportDegree.DeviceId))
                    {
                        continue;
                    }
                    // 计算设备可以覆盖哪些项目
                    var canCoverItems = supportDegree.SuppotedApplyItems;// 当前设备可以覆盖的项目
                    var coveredItems = new List<ExamApplicationDto>();   // 覆盖的项目
                    foreach (var item in uncoveredItems)
                    {
                        if (coveredItems.Contains(item))
                        {
                            coveredItems.Add(item);
                        }
                    }

                    if (coveredItems.Count > bestCoveredCount)
                    { 
                        bestCoveredCount = coveredItems.Count;
                        bestDevice = supportDegree;
                        bestCoveredItems = coveredItems;
                    }
                }

                if (bestCoveredCount == 0)
                {
                    foreach (var uncoveredItem in uncoveredItems)
                    {
                        Logger.LogWarning($"无设备支持检查项目{uncoveredItem.ExamItem.Name},Guid:{uncoveredItem.ExamItem.Id}");
                    }

                    throw new BusinessException(CustomErrorCodes.NoDeviceSupportApplyItem);
                }

                // 从可供选择设备中移除当前设备
                unSelectedDegrees!.Remove(bestDevice);
                var best = new SelectedDevice();
                best.Device = bestDevice.Device;
                best.DeviceId = bestDevice.DeviceId;
                best.ApplyItems = bestCoveredItems;
                // 添加最佳设备到结果中
                selectedDevices.Add(best);
            }

            return selectedDevices;
        }



        /// <summary>
        /// 获取指定设备集合的推荐号池
        /// </summary>
        /// <param name="applyItems">申请项目</param>
        /// <param name="devices">给定设备集合</param>
        /// <returns></returns>
        public async Task<List<PoolDto>> GetSuggestPools(AppointPlan plan,CustomSettingDto globalSetting)
        {
            var deviceTypeId = plan.Devices[0].DeviceTypeId;
            List<PoolDto> pools = new List<PoolDto>();
            foreach (var device in plan.Devices)
            {
                // 规则结构
                var schema = await _schemeFactory.GetDeviceRuleSchema(device.Id!.Value);
                // 生成过滤结构
                var filterSchema = _schemeFactory.GetDeviceFilerSchema(schema);

              
                CustomSettingDto deviceSetting = await _customSettingAppService.GetDeviceSetting(device.Id!.Value);

                var date = DateTime.Now.Date;
                var endDate = date.AddDays((double)globalSetting.AutoAppointableCount!);
                while (date < endDate)
                {
                    await _poolAppService.CreateDeviceAllPoolsOnDate(device.Id!.Value, date);
                    date = date.AddDays(1);
                }

                var poolQuery = await _poolRepository.GetQueryableAsync();
                poolQuery = poolQuery.Where(p => p.DeviceId == device.Id
                                             && p.Date >= DateTime.Now.Date
                                             && p.Date < DateTime.Now.Date.AddDays((double)globalSetting.AutoAppointableCount) // 设定日期范围内的
                                             && DateTime.Now < p.StartTime.AddMinutes(-(double)deviceSetting.LatestAppoint!)    // 满足最晚预约时间的
                                             && p.Current < p.MaxCount                                                           // 还没预约满的
                                             && p.IsActive);                                                                   // 活动的
                // 有时间要求
                if (plan.IsTimeRequired)
                {
                    poolQuery = poolQuery.Where(p => p.StartTime.TimeOfDay <= plan.StartTime
                                                   && p.EndTime.TimeOfDay <= plan.EndTime);
                }

                var allActivePools = await _queryableExecuter.ToListAsync(poolQuery);
                foreach (var pool in allActivePools)
                {
                    var poolDto = _mapper.Map<Pool, PoolDto>(pool);
                    var result = _schemeFactory.ExecuteFilterSchema(poolDto, plan.ApplyItems, filterSchema);
                    // 规则验证成功 true
                    if (result.IsValid)
                    {
                        pools.Add(poolDto);
                    }
                }
            }
            pools = pools.OrderBy(p => p.StartTime).ToList();
            return pools;
        }
    }
}
